<?php

class ExpressionStmt implements ExecutableStmt, ExpNumber
{
	private $rpnQueue;
	private $safe;

	public function __construct (array $rpnQueue, $safe = true)
	{
		$this->rpnQueue = $rpnQueue;
		$this->safe = $safe;
	}

	public function execute (CliContext $context)
	{
		$value = $this->getValue($context);

		$ans = new ExpVariable('ans', $context->getMode());
		$ans->set($value, $context);

		if (is_bool($value))
		{
			return $value ? 'TRUE' : 'FALSE';
		}

		if (CliLib::isNumber($value))
		{
			$value = $context->getMode()->handleResult($value, $context);
		}

		return (string) $value;
	}

	public function getNumber (CliContext $context)
	{
		return CliLib::numberOf($this->getResult($context), $context);
	}

	public function getModeNumber (CliContext $context)
	{
		return CliLib::modeNumberOf($this->getResult($context), $context);
	}

	public function getValue (CliContext $context)
	{
		return CliLib::valueOf($this->getResult($context), $context);
	}

	public function getResult (CliContext $context)
	{
		$stack = array();
		$ex = $this->safe ? 'CliRpnException' : 'CliRpnViolationException';

		// Go through the entire RPN queue
		foreach ($this->rpnQueue as $item)
		{
			// Values or anything that can be a value go on to value stack
			if (CliLib::isValue($item) || $item instanceof StackTerminator)
			{
				$stack[] = $item;
			}
			// Callable items are operated upon
			elseif ($item instanceof ExpCall)
			{
				$count = $item->argCount();
				$args = array();

				// For argument count -1, use arguments until stack terminator
				if ($count == -1)
				{
					while (!(($arg = array_pop($stack)) instanceof StackTerminator))
					{
						if ($arg === null)
						{
							throw new $ex('Unexpected end of value stack');
						}

						$args[] = $arg;
					}
				}
				elseif ($count > 0)
				{
					while ($count-- > 0)
					{
						$arg = array_pop($stack);

						if ($arg === null)
						{
							throw new $ex('Unexpected end of value stack');
						}
						elseif ($arg instanceof StackTerminator)
						{
							throw new $ex('Unexpected value stack terminator');
						}

						$args[] = $arg;
					}
				}

				$args =  new ArgContext($context, array_reverse($args));

				// Return value goes on top of the stack
				$stack[] = $item->call($args);
			}
			else
			{
				throw new CliRpnException(
					"Unknown RPN queue element '" . gettype($item) . "'");
			}
		}

		// At the end we should have only one value on stack if all went well
		if (count($stack) !== 1)
		{
			throw new $ex('Unexpected size of value stack at end of evaluation');
		}

		$last = end($stack);

		if (!CliLib::isValue($last))
		{
			throw new $ex("Unexpected type of last stack value");
		}

		return $last;
	}

	public function __toString ()
	{
		$strings = array();

		foreach ($this->rpnQueue as $item)
		{
			if ($item instanceof ExpressionStmt)
			{
				$strings[] = '{' . $item . '}';
			}
			else
			{
				$strings[] = (string) $item;
			}
		}

		return CliParser::RPNCHAR . ' ' . implode(' ', $strings);
	}
}

?>